#include "luster_lexpr.hh"

#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>

#include "luster_flow.hh"
#include "luster_log.hh"

namespace luster {

static void llist_display(Lexpr const *expr, FILE *fp) {
	bool first;

	fprintf(fp, "(");
	first = true;

	for_range(0, expr->as.exprs.len) {
		if (first) {
			first = false;
		} else {
			fprintf(fp, " ");
		}
		lexpr_display(expr->as.exprs.data + it, fp);
	}
	fprintf(fp, ")");
}

static void display_string(Slice string, FILE *fp) {
	fputc('"', fp);

	for_range(0, string.len) {
		switch (string.data[it]) {
		case '\t':
			fputs("\\t", fp);
			break;
		case '\n':
			fputs("\\n", fp);
			break;
		case '\r':
			fputs("\\r", fp);
			break;
		default:
			/* TODO: more escape chars */
			fputc(string.data[it], fp);
		}
	}
	fputc('"', fp);
}

void lexpr_display(Lexpr const *expr, FILE *fp) {
	switch (expr->tag) {
	case LEXPR_S32:
		fprintf(fp, "%" PRIi32, expr->as.i32);
		break;
	case LEXPR_OP:
	case LEXPR_SYMBOL:
		fwrite(expr->as.string.data, expr->as.string.len, 1, fp);
		break;
	case LEXPR_KEYWORD:
		fputc('#', fp);
		fwrite(expr->as.string.data, expr->as.string.len, 1, fp);
		break;
	case LEXPR_LIST:
		llist_display(expr, fp);
		break;
	case LEXPR_BOOL:
		fprintf(fp, expr->as.yes ? "#t" : "#f");
		break;
	case LEXPR_STRING:
		display_string(expr->as.string, fp);
		break;
	default:
		logfatal("TODO: display l-expr with type: %d", expr->tag);
	}
}

char const *lexpr_tag_name(LexprTag ltype) {
	static Char const *name[] = {
#define _(M_NAME) "LEXPR_" #M_NAME,
		LEXPR_TYPE_MAP(_)
#undef _
	};

	return name[ltype];
}

void lexpr_deinit(Lexpr *expr) {
	switch (expr->tag) {
	case LEXPR_LIST:
		drop(&expr->as.exprs);
		break;
	default:
		break;
	}

	expr->tag = LEXPR_S32;
	expr->as.i32 = -1024;
}

} // namespace luster
